/**
 * @Copyright:Copyright (c) 1992 - 2015
 * @Company: TengTong
 */
package com.tengtong.duang.commons.utils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import org.apache.log4j.Logger;

import com.tengtong.duang.commons.base.Callback;
import com.tengtong.duang.commons.collections.ArrayWrapper;
import com.tengtong.duang.commons.collections.ListUtils;
import com.tengtong.duang.commons.convert.ConversionServiceUtils;

/**
 * Java 类的 Property 工具类
 *
 * @author Jai
 * @since: v1.0
 */
public class PropertyUtils {

	/**
	 * set 方法前缀
	 */
	public final static String SETTER_PREFIX = "set";
	
	/**
	 * get 方法前缀
	 */
	public final static String GETTER_PREFIX = "get";
	public final static String GETTER_IS_PREFIX = "is";
	
	private final static Logger logger = Logger.getLogger(PropertyUtils.class);
	
	/**
	 * 获取传入方法(get/set 方法)对应的属性名
	 *
	 * 如果 method 为 null 则返回 ""；如果 method 不是 set/get 方法，同样返回 "";
	 *
	 * @param method
	 * @return
	 */
	public static String splitPropertyName(Method method){
		if(method == null)
			return "";
		String name = method.getName();
		if(name.startsWith(SETTER_PREFIX) || name.startsWith(GETTER_PREFIX)){
			return StringUtils.firstCharLowerCase(name.substring(3));
		}
		
		if(name.startsWith(GETTER_IS_PREFIX)){
			return StringUtils.firstCharLowerCase(name.substring(2));
		}
		return "";
	}
	
	/**
	 * 给对象属性赋值，在赋值的同时，如果属性的类型与值的类型不匹配，则会对值进行转换。如果 value = null，则不进行任何处理
	 *
	 * @param target
	 * @param property
	 * @param value
	 * @throws Exception
	 */
	public static void setPropertyValue(Object target, String property, Object value) throws Exception {
		if(value == null){
			return;
		}
		Class<?> propertyType = PropertyUtils.getPropertyType(target, property);
		
		if(propertyType == null) //不存在这个属性，或者获取属性类型失败
			return ;
		
		Class<?> valueType = value.getClass();
		Object v = value;
		
		if(!ReflectUtils.isInheritance(propertyType, valueType)){
			if(ClassUtils.isPrimitiveType(propertyType)){
				v = NumberUtils.toWrapperType(v.toString(), propertyType);
				if(v == null)
					v = NumberUtils.toWrapperType("0", propertyType);
			}else if(propertyType.isEnum()) {
				String tmp = (String)v;
				if(tmp != null && tmp.length() > 0){
//					Class cz = Class.forName(propertyType.getName());
//					v = Enum.valueOf(cz, (String)v);
					v = ReflectUtils.getEnumBy(propertyType, (String)v);
				}else
					v = null;
			}else if(propertyType.isArray()){ //
				if(!propertyType.equals(value.getClass())){
					Class<?> tmpTarget = propertyType.getComponentType();
					Object objArray = null;
					if(valueType.isArray()){
						Object[] ds = (Object[])value;
						objArray = Array.newInstance(tmpTarget, ds.length);
						int i=0;
						for(Object d: ds){
							Array.set(objArray, i++, d.toString());
						}
					}else if(value instanceof List){
						List l = (List)value;
						objArray = Array.newInstance(tmpTarget, l.size());
						for(int i=0; i < l.size();i++){
							Array.set(objArray, i, l.get(i).toString());
						}
					}else{
						objArray = Array.newInstance(tmpTarget, 1);
						Array.set(objArray, 0, v);
					}
					v = objArray;
				}
			}else{
				if(v != null){ //对数据进行转换
					Object vl = ConversionServiceUtils.convertTo(v, propertyType);
					if(vl != null){
						v = vl;
					}
				}
			}
		}else{
			v = propertyType.cast(v);
		}
		ReflectUtils.invokeWriteMethod(target, property, new Object[]{v});
	}
	
	/**
	 * 获取对象的属性(property)的值，如果属性不存在，或无法获取，则返回 默认值 defaultValue
	 *
	 * @param target
	 * @param property
	 * @param defaultValue
	 * @return
	 */
	public static Object getPropertyValue(Object target, String property, Object defaultValue){
		try {
			Method method = getGetterMethod(target, property);

			if(method != null){
				return method.invoke(target, new Object[0]);
			}
		}
		catch (Exception e) {
			logger.error("[ML getPropertyValue]==> 取属性[" + property + "]值失败，异常信息：", e);
		}
		return defaultValue;
	}
	
	/**
	 * 获取类定义的所有属性，并以 <名称，类型名> 方式返回结果值
	 *
	 * @param beanClass
	 * @return
	 */
	public static Map<String, String> getAllPropertyFor(Class<?> beanClass) throws Exception{
		Map<String, String> results = new HashMap<String, String>();
		
		PropertyDescriptor[] propertyDescriptors = getBeanPropertyDescriptors(beanClass);
		
		if(propertyDescriptors != null){
			
			//先对 Field 做一个缓存，以加速寻找工作，这是用空间换取时间的例子
			Field[] fields = ReflectUtils.getAllCanModifiedProperty(beanClass);
			Map fieldCache = new HashMap();
			
			for(Field field: fields){
				fieldCache.put(field.getName(), new WeakReference(field.getType()));
			}
			
			for(PropertyDescriptor propertyDescriptor: propertyDescriptors){
				String fieldName = propertyDescriptor.getName();
				WeakReference field = (WeakReference) fieldCache.get(fieldName);
				if(field != null)
					results.put(fieldName, ((Class<?>)field.get()).getCanonicalName());
			}
			fieldCache.clear();
		}
		
		return results;
	}

	/**
	 * 获取类定义的所有属性，并以 <名称，类型> 方式返回结果值
	 *
	 * @param beanClass
	 * @return
	 */
	public static Map<String, Class<?>> getAllPropertyNameAndType(Class<?> beanClass) throws Exception{
		Map<String, Class<?>> results = new HashMap<>();
		
		PropertyDescriptor[] propertyDescriptors = getBeanPropertyDescriptors(beanClass);
		
		if(propertyDescriptors != null){
			
			//先对 Field 做一个缓存，以加速寻找工作，这是用空间换取时间的例子
			Field[] fields = ReflectUtils.getAllCanModifiedProperty(beanClass);
			Map fieldCache = new HashMap();
			
			for(Field field: fields){
				fieldCache.put(field.getName(), new WeakReference(field.getType()));
			}
			
			for(PropertyDescriptor propertyDescriptor: propertyDescriptors){
				String fieldName = propertyDescriptor.getName();
				WeakReference field = (WeakReference) fieldCache.get(fieldName);
				if(field != null)
					results.put(fieldName, (Class<?>)field.get());
			}
			fieldCache.clear();
		}
		
		return results;
	}
	
	/**
	 * 获取一个类的所有 属性描述信息，所谓属性，是指 Bean 的 set/get 描述属性。一个 Field 如果没有 set / get 方法，不算是属性 
	 *
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static PropertyDescriptor[] getBeanPropertyDescriptors(Class<?> beanClass) throws Exception{
		return Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
	}
	
	/**
	 * 获取 Bean 类的所有 读方法
	 *
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static Method[] getBeanPropertyReadMethods(Class<?> beanClass) throws Exception{
		PropertyDescriptor[] propertyDescriptors = getBeanPropertyDescriptors(beanClass);
		
		List<Method> results = ListUtils.newSimpleList();
		if(propertyDescriptors != null){
			for(PropertyDescriptor pd: propertyDescriptors){
				if(pd.getReadMethod() != null)
					results.add(pd.getReadMethod());
			}
		}
		return results.toArray(new Method[results.size()]);
	}
	
	/**
	 * 获取 类的所有可编辑数据域 （不仅限于属性）
	 *
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static String[] getAllPropertyNameFor(Class<?> beanClass) throws Exception {
		Field[] fields = ReflectUtils.getAllCanModifiedProperty(beanClass);
		if(fields != null){
			List<String> results = new ArrayList<String>();
			for(Field field: fields){
				results.add(field.getName());
			}
			return results.toArray(new String[results.size()]);
		}
		return new String[0];
	}
	
	/**
	 * 为 Field 的过滤提供一个过滤器，用来过滤 Property
	 * 
	 * 过滤器的使用:
	 * 
	 *     BaseFilter.accept(field) ，如果需要这个 Property，就让 accept 返回 true, 否则返回 false
	 *
	 * @param beanClass
	 * @param bf
	 * @return
	 * @throws Exception
	 */
	public static String[] getSomePropertyNameFor(Class<?> beanClass, final Predicate<Field> bf) throws Exception {
		final List<Field> fields = new ArrayList<Field>(32);
		
		ReflectUtils.doWithFields(beanClass, new Callback<Field>(){

			@Override
			public void doWith(Field t) {
				if(bf != null && bf.test(t))
					fields.add(t);
			}
			
		}, ReflectUtils.COPYABLE_FIELDS);
		
		String[] results = new String[fields.size()];
		for(int i=0; i < fields.size(); i++){
			results[i] = fields.get(i).getName();
		}
		return results;
	}
	
	/**
	 * 获取 Property 的类型
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static Class<?> getPropertyType(Object target, String propertyName){
		PropertyDescriptor pd;
		try {
			pd = new PropertyDescriptor(propertyName, target.getClass());
			return pd.getPropertyType();
		}
		catch (IntrospectionException e) {
			//logger.error("[Macroview-Lang]==> 获取 Property 类型失败，Property[" + propertyName + "]", e);
		}
		return null;
	}
	
	/**
	 * 判断对象某属性是否属于或继承某种类型
	 *
	 * @param target        对象
	 * @param propertyName  要判断的对象属性
	 * @param type          要判断的类型
	 * @return
	 */
	public static boolean propertyTypeIs(Object target, String propertyName, Class<?> type){
		Class<?> sourceType = getPropertyType(target, propertyName);
		return type.isAssignableFrom(sourceType);
	}
	
	/**
	 * 判断 属性类型是不是字符串类型
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static boolean isString(Object target, String propertyName){
		return propertyTypeIs(target, propertyName, String.class);
	}
	
	/**
	 * 判断 target 是否存在此方法
	 *
	 * @param target
	 * @param methodName
	 * @return
	 */
	public static boolean existMethod(Object target, String methodName, Class<?>...methodParamTypes){
		if(target == null || StringUtils.isEmpty(methodName))
			return false;
		
		try {
			Method method = target.getClass().getMethod(methodName, methodParamTypes);
			
			return method != null;
		}
		catch (Exception e) {}
		return false;
	}
	
	/**
	 * 获取属性的  getter 方法
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static Method getGetterMethod(Class<?> target, String propertyName){
		if(target == null || StringUtils.isEmpty(propertyName))
			return null;
		
		String readMethodName = GETTER_PREFIX + StringUtils.firstCharUpperCase(propertyName);
		Method method = null;
		try {
			method = target.getMethod(readMethodName, new Class[0]);
		}
		catch (Exception e) {
			readMethodName = GETTER_IS_PREFIX + StringUtils.firstCharUpperCase(propertyName);
			
			try {
				method = target.getMethod(readMethodName, new Class[0]);
			}
			catch (Exception ex) {}
		}
		
		return method;
	}
	
	/**
	 * 
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static Method getGetterMethod(Object target, String propertyName){
		return getGetterMethod(target.getClass(), propertyName);
	}
	
	/**
	 * 判断某个注解是否定义在 属性（包括  field/ get 方法）上
	 *
	 * @param target
	 * @param propertyName
	 * @param annotationClass
	 * @return
	 */
	public static boolean isAnnotationPresent(Class<?> target, 
								String propertyName, Class<? extends Annotation> annotationClass){
		Field field = ReflectUtils.findField(target, propertyName, null);
		if(field != null && field.isAnnotationPresent(annotationClass)){
			return true;
		}
		
		Method method = getGetterMethod(target, propertyName);
		if(method != null && method.isAnnotationPresent(annotationClass)){
			return true;
		}
		
		return false;
	}
	
	/**
	 * 从属性（字段和 get方法）中获取指定的注解。如果没有，返回 null
	 *
	 * @param target
	 * @param property
	 * @param annotationClass
	 * @return
	 */
	public static <T extends Annotation> T getPropertyAnnotation(Object target, 
							String property, Class<T> annotationClass){
		Field field = ReflectUtils.findField(target.getClass(), property, null);
		T result = null;
		if(field != null){
			result = field.getAnnotation(annotationClass);
		}
		
		if(result == null){
			Method method = getGetterMethod(target, property);
			if(method != null){
				result = method.getAnnotation(annotationClass);
			}
		}
		
		return result;
	}
	
	/**
	 * 获取属性上定义的所有 Annotation.
	 * 
	 * 我们可以在 属性对应的 类数据域上定义 Annotation，也可以在 get 方法上定义 Annotation。本方法就从这两个地方
	 * 
	 * 收集所有的 Annotation.
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static Annotation[] getPropertyAnnotations(Object target, Field field){
		return getPropertyAnnotations(target.getClass(), field);
	}
	
	/**
	 * 获取 Field 上的注解信息
	 *
	 * @param target
	 * @param field
	 * @return
	 */
	public static Annotation[] getPropertyAnnotations(Class<?> target, Field field){
		List<Annotation> annotations = new ArrayList<Annotation>();
		annotations.addAll( Arrays.asList( field.getAnnotations() ));
		
		PropertyDescriptor pd = null;
		try{ // Field 不一定是个属性
			pd = new PropertyDescriptor(field.getName(), target);
		}catch(Exception e){}
		
		if(pd != null){
			Method readMethod = pd.getReadMethod();
			if(readMethod != null){
				Annotation[] as = readMethod.getAnnotations();

				annotations.addAll( Arrays.asList(as));
			}
		}
		
		return annotations.toArray(new Annotation[annotations.size()]);
	}
	
	/**
	 * 获取类的所有属性名，即由“getXXX” 定义的属性的名称
	 *
	 * @param target
	 * @return
	 */
	public static String[] getAllPropertyNames(Class<?> target) throws Exception{
		final List<String> result = ListUtils.newSimpleList();
		ReflectUtils.doWithPropertys(target, t->result.add(t.getName()));
		return result.toArray(new String[result.size()]);
	}
	
	/**
	 * 判断对象是否存在属性，与判断数据域不同，只要有 set/get 存在，就表示存在此属性。
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static boolean existsProperty(Object target, String propertyName){
		return existsProperty(target.getClass(), propertyName);
	}
	
	/**
	 * 判断对象是否存在属性，与判断数据域不同，只要有 set/get 存在，就表示存在此属性。
	 *
	 * @param target
	 * @param propertyName
	 * @return
	 */
	public static boolean existsProperty(Class<?> targetClass, String propertyName){
		try {
			PropertyDescriptor pd = new PropertyDescriptor(propertyName, targetClass);
			if(pd != null)
				return true;
		}
		catch (IntrospectionException e) {}
		return false;
	}	
}
